package com.csw.cpuoom.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 systemctl status firewalld
 ps -aux | grep java
 kill -9 pid
 netstat -anp |grep 8080
 telnet ip 端口
 查看开放的端口
 cat /etc/ssh/sshd_config

 nohup java -jar xxx.jar > /dev/null 2> /dev/null &
 nohup java -jar xxx.jar > ./output.log &
 nohup java -jar xxx.jar --spring.profiles.active=test2> ./output.log &
 tail -f ./output.log


 #性能排查
 https://blog.csdn.net/weixin_45549188/article/details/129629486

 #arthas教程
 https://arthas.aliyun.com/doc/quick-start.html
 */
@Slf4j
@RestController
@RequestMapping("test")
public class CpuOomController {

    /**
     //54-死锁

     ps -aux | grep java
     #杀死进程继续下一个测试
     kill -9 pid
     #以守护进程启动
     nohup java -jar cpu-oom-test-1.0-SNAPSHOT.jar > ./output.log &

     #如果出现这个工具没有的话
     -bash: pstack: command not found
     sudo yum install -y gdb

     （方法1）
     #直接输入面程序查看死锁
     top
     jstack pid

    （方法2）
     curl -O https://arthas.aliyun.com/arthas-boot.jar
     java -jar arthas-boot.jar
     #输入你的java程序，回车

     #直接输入面程序查看死锁
     thread -b


     */
    // 创建两个共享资源
    private static final String resource1 = new String();
    private static final Object resource2 = new Object();
    @GetMapping("lock")
    public void lock() {

        /**
         * 两个线程 thread1 thread2
         * thread1对resource1上锁，然后尝试获取resource2的锁；
         * thread2对resource2上锁，然后尝试获取resource1的锁；
         * 2个线程会相互等待，出现死锁
         */
        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread 1: Locked resource 1");

                try {
                    Thread.sleep(100); // 假设这里有其他操作，需要一些时间
                } catch (Exception e) {
                    e.printStackTrace();
                }

                System.out.println("Thread 1: Attempting to lock resource 2");

                //尝试索取resource2的锁
                synchronized (resource2) {
                    System.out.println("Thread 1: Locked resource 2");
                }
            }
        }, "thread1");

        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("Thread 2: Locked resource 2");

                try {
                    Thread.sleep(100); // 假设这里有其他操作，需要一些时间
                } catch (Exception e) {
                    e.printStackTrace();
                }

                //尝试索取resource1的锁
                System.out.println("Thread 2: Attempting to lock resource 1");
                synchronized (resource1) {
                    System.out.println("Thread 2: Locked resource 1");
                }
            }
        }, "thread2");

        // 启动线程
        thread1.start();
        thread2.start();
    }


    /**
     ps -aux | grep java
     #杀死进程继续下一个测试
     kill -9 pid
     #以守护进程启动
     nohup java -jar cpu-oom-test-1.0-SNAPSHOT.jar > ./output.log &

     top
     #可以查看内存占用情况
     jmap -heap pid

     #使用arthas查看占用情况
     dashboard

     2-1【推荐】使用程序运行日志直接查看
     这时候其实output.log里面也能看见是哪一行泄漏的
     grep -A 10 -B 1 'OutOfMemory' output.log

     2-2程序在运行之前就指定dump输出（特点文件小，信息精准）
     运行直接指定(设置内存比较小方便测试)
     nohup java -jar -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heap-dump.hprof -Xmx10m xxx.jar > ./output.log &

     #将上面的dump文件考出来

     #用visualvm工具进行分析，没有的进行下载
     https://visualvm.github.io/index.html

     用工具加载之后，首页就有泄漏的红色标记
     或者summary换成thread 视图选择文本直接搜memory就可以看到了

     2-3 在运行的时候手动导出堆信息（特点文件大，没有直接溢出信息）【不推荐】
     #在运行导出日志文件【直接导出的文件大，而且查不见泄漏的进程，日志可以】
     jmap -dump:format=b,file=./dump.hprof pid

     #将上面的dump文件考出来

     https://eclipse.dev/mat/
     #必须使用Eclipse MAT工具进行分析才行
     （工具下载不下来换镜像，jdk必须17以上）
     #Spring推荐的Liberica Open JDK下载地址-贝尔实验室版本
     https://bell-sw.com/pages/downloads/#jdk-8-lts

     往下滑会提示有提示，只能大致推断出是那个类，不能具体到哪一行
     Thread Stack信息里面显示也是空空如也
     如果结合visualvm分析的话，只能说也很难推断出是哪一行吧，不推荐
     */
    public static List<byte[]> oomList = new ArrayList<>();

    @GetMapping("oom")
    public String oom() {
        while (true) {
            //不断向列表中添加大对象, 10MB
            oomList.add(new byte[1024 * 1024 * 10]);
        }
    }

    /**
     //56-cpu飙升
     //常规排查和arthas-57

     ps -aux | grep java
     #杀死进程继续下一个测试
     kill -9 pid
     #以守护进程启动
     nohup java -jar cpu-oom-test-1.0-SNAPSHOT.jar > ./output.log &

     3-1常规方法（方法1）
     #使用top命令找到cpu飙升进程id
     top
     #根据进程id找到导致cpu飙升的线程
     ps H -eo pid,tid,%cpu | grep 进程id
     #将线程id转换为16进制
     printf '0x%x\n' 线程id
     #根据线程定位问题代码
     jstack 进程id | grep 16进制线程id -A 20

     简化【推荐，省事】

     创建一个脚本直接执行，把上面几个步骤合起来
     #!/bin/bash
     # 获取用户输入的进程 ID
     read -p "请输入要分析的Java进程ID: " pid
     # 查找该进程下CPU使用率最高的线程（按CPU使用率降序排序取第一条）
     top_thread=$(ps H -eo pid,tid,%cpu | grep $pid | sort -k3 -nr | head -n 1 | awk '{print $2}')
     # 将线程ID转换为十六进制
     hex_thread=$(printf '0x%x\n' $top_thread)
     # 使用jstack查找对应线程的栈信息并输出
     jstack $pid | grep $hex_thread -A 20


     3-2使用arthas(方法2)【推荐快】
     curl -O https://arthas.aliyun.com/arthas-boot.jar
     java -jar arthas-boot.jar
     #输入你的java程序，回车
     thread
     #找到占用最高cpu的线程id

     thread 线程id
     */

    @GetMapping("cpu")
    public void cpu() {
        System.out.println("程序已启动");
        while (true) {
            ;
        }
    }

    /**
     //58-60-接口响应慢-arthas

     ps -aux | grep java
     #杀死进程继续下一个测试
     kill -9 pid
     #以守护进程启动
     nohup java -jar cpu-oom-test-1.0-SNAPSHOT.jar > ./output.log &

     curl -O https://arthas.aliyun.com/arthas-boot.jar
     java -jar arthas-boot.jar
     #输入你的java程序，回车

     #接口响应慢排查
     【会显示总共执行时间是多少毫秒，每个步骤是多少毫秒】
     trace com.csw.cpuoom.controller.CpuOomController slow  -n 5 --skipJDKMethod false
     然后再掉一下接口观察输出

     #检查代码未生效
     【会反编译类获取线上的代码，能看到线上代码对不对】
     jad com.csw.cpuoom.controller.CpuOomControllerjad

     #检查方法执行异常
     【相当于说能够看到方法的实时请求和返回值】
     watch com.csw.cpuoom.controller.CpuOomController add "{params,returnObj}" -x 2 -n 999
     */
    @GetMapping("slow")
    public void slow() throws InterruptedException {
        log.info("start");
        this.m1(100, "t1");
        this.m1(200, "t2");
        this.m1(1000, "t3");
        log.info("end");
    }

    private void m1(int minus, String name) throws InterruptedException {
        //休眠100毫秒
        TimeUnit.MILLISECONDS.sleep(minus);
        log.info(name);
    }


    /**
     62-查看方法执行异常-atthas
     */
    @GetMapping("err")
    public  void err() throws InterruptedException {
        System.out.println("程序已启动");
        for (int i = 1; i < 1000000; i++) {
            add(i, i + 1);
            TimeUnit.SECONDS.sleep(1);
        }
    }
    public static int add(int a, int b) {
        return a + b;
    }
}
